<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        table {
            margin: 0 auto;
            background-color: skyblue;
            border-collapse: collapse;
        }

        td {
            width: 20px;
            height: 20px;
        }

        .bgSnake {
            background-color: red;
        }

        .bgFood {
            background-color: #666;
        }

        .bgHead {
            background-color: springgreen;
        }
    </style>
</head>

<body>
    <table></table>
    <script>
        /*  
        bug 1:食物若生成在蛇肚子里则无法生成新的食物
        bug 2:按键事件做了互斥效果，即当前向右运动时无法按左（用一个变量保存当前运动状态）
              计时器是1s执行一次，故此期间可以触发多次按键事件，达不到互斥效果
        */

        class Snake {
            position = [
                { top: 0, left: 2 },
                { top: 0, left: 1 },
                { top: 0, left: 0 },
            ]
        }

        class Game {
            t = null
            table = null
            snake = null
            snakeList = null
            food = null
            direction = 'right'

            data = [
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
                [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
            ]
            constructor(table, snakeClass) {
                this.table = table;
                this.snake = new snakeClass();
                this.snakeList = this.snake.position;
            }
            //将数据渲染到页面
            rander() {
                this.table.innerHTML = ''
                this.data.forEach(v => {
                    let tr = document.createElement('tr')
                    this.table.appendChild(tr)
                    v.forEach(v => {
                        let td = document.createElement('td')
                        tr.appendChild(td)
                        if (v.state === 1) {
                            td.className = "bgSnake"
                        } else if (v.state === 2) {
                            td.className = "bgFood"
                        } else if (v.state === 3) {
                            td.className = "bgHead"
                        }
                    })
                });
            }

            //初始化游戏
            init() {
                this.setDataState(this.snakeList, 1)
                this.createFood()
                if (!this.t) {
                    this.t = setInterval(() => {
                        this.move(this.direction)
                    }, 100);
                }
                this.rander();
            }

            //给data安装坐标设置状态
            setDataState(list, state) {
                let x, y;
                if (state == 1) {
                    list.forEach(v => {
                        x = v.left
                        y = v.top
                        this.data[y][x].state = state
                    });
                } else {
                    x = list.left
                    y = list.top
                    this.data[y][x].state = state
                }
            }

            //移动，to是方向
            move(to) {
                let x, y
                switch (to) {
                    case "up": y = -1; x = 0; break;
                    case "down": y = 1; x = 0; break;
                    case "left": y = 0; x = -1; break;
                    case "right": y = 0; x = 1; break;
                }
                x += this.snakeList[0].left
                y += this.snakeList[0].top
                if (this.checkTouch(x, y)) {
                    alert('游戏结束')
                    clearInterval(this.t)
                    return
                } else if (this.data[y][x].state != 2) {
                    this.setDataState(this.snakeList.pop(), 0)
                } else {
                    this.createFood()
                }
                this.snakeList.unshift({ top: y, left: x })
                this.setDataState(this.snakeList, 1)
                this.setDataState({ top: y, left: x }, 3)
                this.rander();
            }

            //创建食物坐标
            createFood() {
                let x = Math.floor(Math.random() * this.data[0].length)
                let y = Math.floor(Math.random() * this.data.length)
                console.log(this.snakeList.indexOf({ top: y, left: x }));
                this.setDataState({ top: y, left: x }, 2)
            }

            //触碰检测
            checkTouch(x, y) {
                let flag = false
                if (y == this.data.length || y < 0 || x == this.data[0].length || x < 0)
                    flag = true
                else if (this.data[y][x].state == 1)
                    flag = true
                return flag
            }

            //开始游戏
            start() {
                this.init();
            }
        }
        let table = document.querySelector('table')
        let game = new Game(table, Snake)
        //键盘事件
        document.body.onkeydown = e => {
            switch (e.keyCode) {
                case 87:
                    if (game.direction != 'down')
                        game.direction = 'up'
                    break
                case 83:
                    if (game.direction != 'up')
                        game.direction = 'down'
                    break
                case 65:
                    if (game.direction != 'right')
                        game.direction = 'left'
                    break
                case 68:
                    if (game.direction != 'left')
                        game.direction = 'right'
            }
        }
        game.start();
    </script>
</body>

</html>